using System;
using System.IO;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Markup;
using log4net;
using Triton.Bot;
using Triton.Bot.Settings;
using Triton.Common;
using Triton.Game;
using Logger1 = Triton.Common.LogUtilities.Logger;
using HearthMirror;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Threading.Tasks;
using System.Reflection;
using Triton.Game.Mapping;
using Triton.Game.Mono;

//!CompilerOption|AddRef|System.ServiceModel.dll
//!CompilerOption|AddRef|HearthMirror.dll

namespace Monitor
{
    public class Monitor : IPlugin
    {
        private static readonly ILog Log = Logger1.GetLoggerInstanceForType();
        Timer _expTimer;
        static MonitorService Service;
        static ServiceHost Host;
        private bool _enabled;
        ProxyGameState _state;

        #region Implementation of IPlugin

        /// <summary> The name of the plugin. </summary>
        public string Name
        {
            get { return "监控插件"; }
        }

        /// <summary> The description of the plugin. </summary>
        public string Description
        {
            get { return "监控胜负场、胜率、投降数、经验、等级."; }
        }

        /// <summary>The author of the plugin.</summary>
        public string Author
        {
            get { return "DemonW"; }
        }

        /// <summary>The version of the plugin.</summary>
        public string Version
        {
            get { return "0.0.1"; }
        }

        /// <summary>Initializes this object. This is called when the object is loaded into the bot.</summary>
        public void Initialize()
        {
            Log.DebugFormat("[监控插件] 初始化");
            TritonHs.OnGuiTick += TritonHsOnOnGuiTick;
            BotManager.PostTick += BotManager_PostTick;
            BotManager.PostStop += BotManager_PostStop;
            GameEventManager.FatalError += GameEventManager_FatalError;
            if (!MainSettings.Instance.EnabledPlugins.Contains(this.Name))
                MainSettings.Instance.EnabledPlugins.Add(this.Name);
            InitWcfService();
            //var thsType = typeof(TritonHs);
            //var ps = thsType.GetProperties(BindingFlags.NonPublic | BindingFlags.Static);
            //foreach (var p in ps)
            //{
            //    if (p.Name == "Class276_0")
            //    {
            //        var class276 = p.GetValue(null);
            //        var class276Type = p.PropertyType;
            //        var sps = class276Type.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance);
            //        var sfs = class276Type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
            //        var int0 = 0;
            //        foreach (var f in sfs)
            //        {
            //            if (f.FieldType == typeof(IntPtr))
            //            {
            //                if (f.Name == "intptr_0")
            //                {
            //                    int0 = (int)(IntPtr)f.GetValue(class276);
            //                }
            //                else
            //                {
            //                    var v = f.GetValue(class276);
            //                    Log.InfoFormat("this.{0} = this.intptr_0 + {1};", f.Name, (int)(IntPtr)v - int0);
            //                }
            //            }
            //        }
            //    }
            //}
        }

        /// <summary>Deinitializes this object. This is called when the object is being unloaded from the bot.</summary>
        public void Deinitialize()
        {
            TritonHs.OnGuiTick -= TritonHsOnOnGuiTick;
            BotManager.PostTick -= BotManager_PostTick;
            BotManager.PostStop -= BotManager_PostStop;
            GameEventManager.FatalError -= GameEventManager_FatalError;
        }

        private void BotManager_PostStop(IBot bot)
        {
            if (Service != null)
            {
                Service.OnBotStoped();
            }
        }

        /// <summary> The plugin start callback. Do any initialization here. </summary>
        public void Start()
        {
            Log.DebugFormat("[监控插件] 开启");

            GameEventManager.StartingNewGame += GameEventManager_StartingNewGame;
            GameEventManager.MulliganConfirm += GameEventManager_MulliganConfirm;
            GameEventManager.GameOver += GameEventManager_OnGameOver;
            Service.SetStats(GetStats());
        }

        /// <summary> The plugin stop callback. Do any pre-dispose cleanup here. </summary>
        public void Stop()
        {
            Log.DebugFormat("[监控插件]停止");

            GameEventManager.StartingNewGame -= GameEventManager_StartingNewGame;
            GameEventManager.MulliganConfirm -= GameEventManager_MulliganConfirm;
            GameEventManager.GameOver -= GameEventManager_OnGameOver;

        }

        private void InitWcfService()
        {
            System.Threading.Tasks.Task.Factory.StartNew(() =>
            {
                if (Host != null)
                    return;

                var baseAddr = new Uri("net.pipe://localhost/PipeHearthbuddyMonitor");
                //proxy binding
                var proxyBinding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
                proxyBinding.ReceiveTimeout = TimeSpan.MaxValue;
                proxyBinding.MaxBufferSize = int.MaxValue;
                proxyBinding.MaxReceivedMessageSize = int.MaxValue;

                //mex binding
                var mexBinding = MetadataExchangeBindings.CreateMexNamedPipeBinding();

                Service = new MonitorService(Log, StartBot);
                Host = new ServiceHost(Service, baseAddr);
                Host.Description.Behaviors.Add(new ServiceMetadataBehavior());
                Host.AddServiceEndpoint(typeof(IMonitorService), proxyBinding, "Monitor");
                Host.AddServiceEndpoint(typeof(IMetadataExchange), mexBinding, "Mex");
                Host.Opened += Host_Opened;
                Host.Open();
            }, System.Threading.Tasks.TaskCreationOptions.LongRunning);
        }

        private void Host_Opened(object sender, EventArgs e)
        {
            Log.Info("WCF服务已启动");
        }

        /// <summary> The plugin tick callback. Do any update logic here. </summary>
        public void Tick()
        {
            
        }



        public JsonSettings Settings
        {
            get { return MonitorSettings.Instance; }
        }

        private UserControl _control;

        /// <summary> The plugin's settings control. This will be added to the Hearthbuddy Settings tab.</summary>
        public UserControl Control
        {
            get
            {
                if (_control != null)
                {
                    return _control;
                }

                using (var fs = new FileStream(@"Plugins\Monitor\SettingsGui.xaml", FileMode.Open))
                {
                    var root = (UserControl)XamlReader.Load(fs);

                    // Your settings binding here.

                    if (!Wpf.SetupTextBoxBinding(root, "WinsTextBox", "Wins",
                        BindingMode.TwoWay, MonitorSettings.Instance))
                    {
                        Log.DebugFormat("[SettingsControl] SetupTextBoxBinding failed for 'WinsTextBox'.");
                        throw new Exception("The SettingsControl could not be created.");
                    }

                    if (!Wpf.SetupTextBoxBinding(root, "LossesTextBox", "Losses",
                        BindingMode.TwoWay, MonitorSettings.Instance))
                    {
                        Log.DebugFormat("[SettingsControl] SetupTextBoxBinding failed for 'LossesTextBox'.");
                        throw new Exception("The SettingsControl could not be created.");
                    }

                    if (!Wpf.SetupTextBoxBinding(root, "ConcedesTextBox", "Concedes",
                        BindingMode.TwoWay, MonitorSettings.Instance))
                    {
                        Log.DebugFormat("[SettingsControl] SetupTextBoxBinding failed for 'ConcedesTextBox'.");
                        throw new Exception("The SettingsControl could not be created.");
                    }

                    if (!Wpf.SetupTextBoxBinding(root, "LevelTextBox", "Level",
                        BindingMode.TwoWay, MonitorSettings.Instance))
                    {
                        Log.DebugFormat("[SettingsControl] SetupTextBoxBinding failed for 'LevelTextBox'.");
                        throw new Exception("The SettingsControl could not be created.");
                    }

                    if (!Wpf.SetupTextBoxBinding(root, "XpTextBox", "Xp",
                        BindingMode.TwoWay, MonitorSettings.Instance))
                    {
                        Log.DebugFormat("[SettingsControl] SetupTextBoxBinding failed for 'XpTextBox'.");
                        throw new Exception("The SettingsControl could not be created.");
                    }

                    if (!Wpf.SetupTextBoxBinding(root, "XpNeededTextBox", "XpNeeded",
                        BindingMode.TwoWay, MonitorSettings.Instance))
                    {
                        Log.DebugFormat("[SettingsControl] SetupTextBoxBinding failed for 'XpNeededTextBox'.");
                        throw new Exception("The SettingsControl could not be created.");
                    }

                    // Your settings event handlers here.

                    var resetButton = Wpf.FindControlByName<Button>(root, "ResetButton");
                    resetButton.Click += ResetButtonOnClick;

                    //var addWinButton = Wpf.FindControlByName<Button>(root, "AddWinButton");
                    //addWinButton.Click += AddWinButtonOnClick;

                    //var addLossButton = Wpf.FindControlByName<Button>(root, "AddLossButton");
                    //addLossButton.Click += AddLossButtonOnClick;

                    //var addConcedeButton = Wpf.FindControlByName<Button>(root, "AddConcedeButton");
                    //addConcedeButton.Click += AddConcedeButtonOnClick;

                    //var removeWinButton = Wpf.FindControlByName<Button>(root, "RemoveWinButton");
                    //removeWinButton.Click += RemoveWinButtonOnClick;

                    //var removeLossButton = Wpf.FindControlByName<Button>(root, "RemoveLossButton");
                    //removeLossButton.Click += RemoveLossButtonOnClick;

                    //var removeConcedeButton = Wpf.FindControlByName<Button>(root, "RemoveConcedeButton");
                    //removeConcedeButton.Click += RemoveConcedeButtonOnClick;

                    UpdateMainGuiStats();

                    _control = root;
                }

                return _control;
            }
        }

        /// <summary>Is this plugin currently enabled?</summary>
        public bool IsEnabled
        {
            get { return _enabled; }
        }

        /// <summary> The plugin is being enabled.</summary>
        public void Enable()
        {
            Log.DebugFormat("[监控插件] 启用");
            _enabled = true;
        }

        /// <summary> The plugin is being disabled.</summary>
        public void Disable()
        {
            Log.DebugFormat("[监控插件] 禁用");
            _enabled = false;
        }

        #endregion

        #region Implementation of IDisposable

        #endregion

        #region Override of Object

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return Name + ": " + Description;
        }

        #endregion

        private void GameEventManager_StartingNewGame(object sender, StartingNewGameEventArgs e)
        {
            _state = ProxyGameState.StartingNewGame;
            if (Service != null)
            {
                var stats = GetStats();
                if (stats != null)
                {
                    Service.OnStatsChanged(stats);
                }
                Service.OnStartNewGame();
            }
        }

        private void GameEventManager_MulliganConfirm(object sender, MulliganConfirmEventArgs e)
        {
            _state = ProxyGameState.Mulligan;
            if (Service != null)
            {
                var stats = GetStats();
                if (stats != null)
                {
                    Service.OnStatsChanged(stats);
                }
                Service.OnMulliganConfirm();
            }
        }

        private void GameEventManager_OnGameOver(object sender, GameOverEventArgs gameOverEventArgs)
        {
            _state = ProxyGameState.GameOver;
            if (gameOverEventArgs.Result == GameOverFlag.Victory)
            {
                MonitorSettings.Instance.Wins++;
                UpdateMainGuiStats();
            }
            else if (gameOverEventArgs.Result == GameOverFlag.Defeat)
            {
                if (gameOverEventArgs.Conceded)
                {
                    MonitorSettings.Instance.Concedes++;
                }
                else
                {
                    MonitorSettings.Instance.Losses++;
                }
                UpdateMainGuiStats();
            }
            if (Service != null)
            {
                var e = new ProxyGameOverEventArgs();
                e.Result = (ProxyGameOverFlag)gameOverEventArgs.Result;
                e.Conceded = gameOverEventArgs.Conceded;
                Service.OnGameOver(e);
                var stats = GetStats();
                if (stats != null)
                {
                    Service.OnStatsChanged(stats);
                }
            }
        }

        private void GameEventManager_FatalError(object sender, FatalErrorEventArgs e)
        {
            Log.Info("Fatal----------------");
            if (Service != null)
            {
                Service.OnFatalError();
            }
        }

        private void ResetButtonOnClick(object sender, RoutedEventArgs routedEventArgs)
        {
            MonitorSettings.Instance.Wins = 0;
            MonitorSettings.Instance.Losses = 0;
            MonitorSettings.Instance.Concedes = 0;
            UpdateMainGuiStats();
        }

        private void BotManager_PostTick(IBot bot)
        {
            TryPressReconnectButton();
        }

        private void UpdateMainGuiStats()
        {
            Application.Current.Dispatcher.BeginInvoke(new Action(() =>
            {
                var leftControl = Wpf.FindControlByName<Label>(Application.Current.MainWindow, "StatusBarLeftLabel");
                var rightControl = Wpf.FindControlByName<Label>(Application.Current.MainWindow, "StatusBarRightLabel");

                if (rightControl != null)
                {
                    if (MonitorSettings.Instance.Wins + MonitorSettings.Instance.Losses == 0)
                    {
                        if (MonitorSettings.Instance.Concedes == 0)
                        {
                            rightControl.Content = string.Format("(没有对局信息)");
                        }
                        //else
                        //{
                        //    rightControl.Content = string.Format("[{0} 投降局数]", MonitorSettings.Instance.Concedes);
                        //}
                    }
                    else
                    {
                        rightControl.Content = string.Format("{0}级({1}/{2}) {3}胜/{4}总(胜率:{5:0.00}%)[{6}投] ",
                            MonitorSettings.Instance.Level,
                            MonitorSettings.Instance.Xp,
                            MonitorSettings.Instance.XpNeeded,
                            MonitorSettings.Instance.Wins,
                            MonitorSettings.Instance.Wins + MonitorSettings.Instance.Losses,
                            100.0f * (float)MonitorSettings.Instance.Wins /
                            (float)(MonitorSettings.Instance.Wins + MonitorSettings.Instance.Losses),
                            MonitorSettings.Instance.Concedes);

                        Log.InfoFormat("[监控插件] 合计: {0}", rightControl.Content);
                    }

                    // Force a save all.
                    Configuration.Instance.SaveAll();
                }
            }));
        }

        private void TritonHsOnOnGuiTick(object sender, GuiTickEventArgs guiTickEventArgs)
        {
            // Only update if we're actually enabled.
            if (IsEnabled)
            {
                Application.Current.Dispatcher.BeginInvoke(new Action(() =>
                {
                    var leftControl = Wpf.FindControlByName<Label>(Application.Current.MainWindow, "StatusBarLeftLabel");
                    if (leftControl != null)
                    {
                        leftControl.Content = string.Format("运行时间: {0}", TritonHs.Runtime.Elapsed.ToString("h'小时 'm'分 's'秒'"));
                    }
                }));
            }
        }
        private ProxyStats GetStats()
        {
            try
            {
                if (Status.GetStatus().MirrorStatus != HearthMirror.Enums.MirrorStatus.Ok)
                    return null;
                var reward = Reflection.GetRewardTrackData();
                var sceneMode = Reflection.GetCurrentSceneMode();
                if (reward == null)
                    return null;
                MonitorSettings.Instance.Level = reward.Level;
                MonitorSettings.Instance.Xp = reward.Xp;
                MonitorSettings.Instance.XpNeeded = reward.XpNeeded;
                var stats = new ProxyStats
                {
                    Wins = MonitorSettings.Instance.Wins,
                    Losses = MonitorSettings.Instance.Losses,
                    Concedes = MonitorSettings.Instance.Concedes,
                    Level = MonitorSettings.Instance.Level,
                    Xp = MonitorSettings.Instance.Xp,
                    XpNeeded = MonitorSettings.Instance.XpNeeded,
                    SceneMode = (ProxySceneMode)sceneMode,
                    GameState = _state
                };
                return stats;
            }
            catch (Exception ex)
            {
                Log.WarnFormat("{0}\r\n{1}", ex.Message, ex.StackTrace);
                return null;
            }
        }

        bool isReconnectButtonClicked = false;
        private void TryPressReconnectButton()
        {
            try
            {
                var diagMgr = Triton.Game.Mapping.DialogManager.Get();
                if (diagMgr.ShowingDialog() && diagMgr.m_currentDialog.RealClassName == "ReconnectHelperDialog")
                {
                    var type = typeof(MonoClass);
                    foreach (var m in type.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance))
                    {
                        if (!m.ContainsGenericParameters)
                            continue;
                        var parameters = m.GetParameters();
                        if (parameters.Length == 1 && parameters[0].ParameterType.Name.ToLower() == "string")
                        {
                            try
                            {
                                var obj = m.MakeGenericMethod(typeof(UIBButton)).Invoke(diagMgr.m_currentDialog, new object[] { "m_choiceOneButton" });
                                if (obj is UIBButton)
                                {
                                    var btn = obj as UIBButton;
                                    if (btn.IsEnabled() && !isReconnectButtonClicked)
                                    {
                                        Log.Info("[监控插件] now clicking on the \"reconnect\" button.");
                                        btn.TriggerPress();
                                        btn.TriggerRelease();
                                        diagMgr.m_currentDialog.Hide();
                                        isReconnectButtonClicked = true;
                                    }
                                    return;
                                }
                            }
                            catch { }
                        }
                    }
                }
                else
                {
                    isReconnectButtonClicked = false;
                }
            }
            catch (Exception ex)
            {
                Log.WarnFormat("{0}\r\n{1}", ex.Message, ex.StackTrace);
            }
        }

        private void StartBot()
        {
            Task.Factory.StartNew(() =>
            {
                TimeSpan timeout = TimeSpan.FromSeconds(30);
                while ((BotManager.IsRunning || !this.IsEnabled) && timeout > TimeSpan.Zero)
                {
                    timeout -= TimeSpan.FromSeconds(1);
                    Thread.Sleep(1000);
                }
                BotManager.Start();
            }, TaskCreationOptions.LongRunning);
        }
    }
}
